package com.docking.heylo.service.impl;

import com.docking.heylo.ThreadContext;
import com.docking.heylo.constant.SystemConstant;
import com.docking.heylo.dao.entity.*;
import com.docking.heylo.dao.mapper.BigVApplyMapper;
import com.docking.heylo.dao.mapper.PersonalInformationMapper;
import com.docking.heylo.dao.mapper.UserApertureSetMapper;
import com.docking.heylo.dao.mapper.UserMapper;
import com.docking.heylo.enums.EUserStatus;
import com.docking.heylo.exception.GeneralException;
import com.docking.heylo.pojo.Audience;
import com.docking.heylo.pojo.SimpleUser;
import com.docking.heylo.service.IAccountService;
import com.docking.heylo.service.ICacheService;
import com.docking.heylo.service.IPostService;
import com.docking.heylo.service.IUserService;
import com.docking.heylo.utils.AliYunSMS;
import com.docking.heylo.utils.HeyloMessageSource;
import com.docking.heylo.utils.JwtHelper;
import com.docking.heylo.web.dto.*;
import com.docking.heylo.web.utils.MD5;
import com.docking.heylo.web.vo.LoginResult;
import io.rong.models.response.TokenResult;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Service
public class UserServiceImpl implements IUserService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private BigVApplyMapper applyMapper;

    @Resource
    private PersonalInformationMapper personalInformationMapper;

    @Autowired
    private HeyloMessageSource ms;

    @Autowired
    private Audience audience;

    @Autowired
    private IPostService postService;

    @Autowired
    private RongCloudImServerImpl rongCloudImServer;

    @Autowired
    private IAccountService accountService;

    @Autowired
    private ICacheService cacheService;

    @Resource
    private UserApertureSetMapper userApertureSetMapper;



    @Override
    public void sendEnrollmentVerCode(EnrollmentDto enrollmentDto) {
        if(phoneExist(enrollmentDto.getPhone())){
            throw new GeneralException(ms.getMessage(SystemConstant.phone_exist));
        }

        String code = sixNumber();
        AliYunSMS.sendRegisterCustomerUserSMS(enrollmentDto.getPhone(), code);
        cacheService.sendSmsVerCode(ThreadContext.ip(), enrollmentDto.getPhone(), code);
//        cacheService.sendSmsVerCode(ThreadContext.ip(), enrollmentDto.getPhone(), "123456");
    }


    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public LoginResult enrollment(EnrollmentDto enrollmentDto) {
        if(selectCountByPhoneAndStateNotOne(enrollmentDto.getPhone()) >= 1){
            throw new GeneralException(ms.getMessage(SystemConstant.phone_exist));
        }

        String verCode = cacheService.getSmsVerCode(enrollmentDto.getPhone());
        if(StringUtils.isEmpty(verCode)){
            throw new GeneralException(ms.getMessage("sms.code.invalid"));
        }else if(!verCode.equals(enrollmentDto.getVerCode())){
            throw new GeneralException(ms.getMessage("sms.code.error"));
        }

        User dbUser = selectUserByPhone(enrollmentDto.getPhone());
        if(dbUser == null){
            User user = new User();
            user.setPhone(enrollmentDto.getPhone());
            user.setRegTime(new Date());
            user.setStatus(EUserStatus.create.getState());
            userMapper.insert(user);

            dbUser = selectUserByPhone(enrollmentDto.getPhone());

            // 注册容云
            TokenResult result = rongCloudImServer.register(dbUser.getId().toString(), null, null);

            User finalDbUser = dbUser;
            userMapper.updateByPrimaryKeySelective(new User(){{
                setId(finalDbUser.getId());
                setrCToken(result.getToken());
                setImId(result.getUserId());
            }});
        }

        LoginResult result = bindLoginResult(dbUser, null);
        result.setToken(constructUserToken(dbUser.getId()));
        return result;
    }

    public long selectCountByPhone(String phone){
        UserExample example = new UserExample();
        UserExample.Criteria criteria = example.createCriteria();
        criteria.andPhoneEqualTo(phone);
        return userMapper.countByExample(example);
    }

    public long selectCountByPhoneAndStateNotOne(String phone){
        UserExample example = new UserExample();
        UserExample.Criteria criteria = example.createCriteria();
        criteria.andStatusNotEqualTo(EUserStatus.create.getState());
        criteria.andPhoneEqualTo(phone);
        return userMapper.countByExample(example);
    }



    @Override
    public LoginResult login(EnrollmentDto enrollmentDto) {
        User user = selectUserByPhone(enrollmentDto.getPhone());

        // 账户不存在
        if(user == null){
            throw new GeneralException(ms.getMessage(SystemConstant.phone_password_error));
        }

        // 账户未设置密码
        if(StringUtils.isEmpty(user.getPassword())){
            throw new GeneralException(ms.getMessage(SystemConstant.password_not_bind));
        }

        // 密码错误
        if(!user.getPassword().equals(encryptionPassword(enrollmentDto.getPassword()))){
            throw new GeneralException(ms.getMessage(SystemConstant.phone_password_error));
        }

        //
        PersonalInformation pi = personalInformationMapper.selectByPrimaryKey(user.getId());

        LoginResult result = bindLoginResult(user, pi);
        result.setToken(constructUserToken(user.getId()));
        return result;
    }

    /***
     * 注册使用
     * @param phone
     * @return
     */
    public boolean phoneExist(String phone){
        UserExample example = new UserExample();
        UserExample.Criteria criteria = example.createCriteria();
        criteria.andPhoneEqualTo(phone);
        criteria.andStatusNotEqualTo(EUserStatus.create.getState());
        return userMapper.countByExample(example) > 0;
    }

    private User selectUserByPhone(String phone){
        UserExample userExample = new UserExample();
        UserExample.Criteria criteria = userExample.createCriteria();
        criteria.andPhoneEqualTo(phone);
        List<User> users = userMapper.selectByExample(userExample);
        return users.size() == 1 ? users.get(0) : null;
    }

    private LoginResult bindLoginResult(User user, PersonalInformation pi){
        LoginResult result = new LoginResult();
        result.setUserId(user.getId());
        result.setImToken(user.getrCToken());
        result.setStatus(user.getStatus());

        if(pi != null){
            BeanUtils.copyProperties(pi, result);
            result.setType(user.getType());
        }

        return result;
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void createArchive(UserArchive userArchive) {
        User user = userMapper.selectByPrimaryKey(ThreadContext.userId());
        user.setType(userArchive.getProfession());
        if(user.getStatus() != EUserStatus.set_password.getState()){
            throw new GeneralException(ms.getMessage("duplicate_submission"));
        }

        // steep1 设置用户基本信息
        PersonalInformation pi = new PersonalInformation();
        pi.setId(user.getId());
        pi.setRealName(userArchive.getRealName());
        pi.setSex(userArchive.getGender());
        pi.setAvatar(userArchive.getAvatar());
        pi.setSignature(userArchive.getDescription());
        pi.setEquipment(userArchive.getEquipment());
        pi.setStyle(userArchive.getStyles());
        pi.setQ1((int) userArchive.getQ1());
        pi.setQ2((int) userArchive.getQ2());
        pi.setMode((int) userArchive.getMode());
        pi.setUserId(user.getId());
        pi.setShowModel(3);
        pi.setV((byte) 0);
        personalInformationMapper.insertSelective(pi);

        //steep 3 保存作品集合
        if(userArchive.getPost() != null){
            ThreadContext.authorize.get().getUser().setType(userArchive.getProfession());
            postService.savePost(userArchive.getPost());
        }

        // steep 4 容云
        rongCloudImServer.update(user.getId().toString(), pi.getRealName(), pi.getAvatar());

        // steep 5 开通个人账户
        accountService.openAccount(user.getId());

        // 清理缓存
        cacheService.removeCache("userInfoCache", user.getId());

        // 修改用户状态
        userMapper.updateByPrimaryKeySelective(new User(){{
            setId(user.getId());
            setStatus(EUserStatus.complete.getState());
            setType(userArchive.getProfession());
        }});
    }

    @Override
    public void setPassword(EnrollmentDto enrollmentDto) {
        User user = userMapper.selectByPrimaryKey(ThreadContext.userId());
        if(user.getStatus() != EUserStatus.create.getState()){
            throw new GeneralException(ms.getMessage("duplicate_submission"));
        }

        // 设置密码
        userMapper.updateByPrimaryKeySelective(new User(){{
            setId(user.getId());
            setPassword(encryptionPassword(enrollmentDto.getPassword()));
            setStatus(EUserStatus.set_password.getState());
        }});
    }

    /***
     * 两次MD5
     *
     * @return
     */
    private String encryptionPassword(String password){
        return MD5.getMD5(MD5.getMD5(password));
    }

    /***
     * 构建用户登录Token
     *
     * @param userId
     * @return
     */
    private String constructUserToken(int userId){
        return JwtHelper.constructJWT(userId, new Date(),
                audience.getClientId(), audience.getName(), audience.getExpiresSecond(), audience.getBase64Secret());
    }

    @Override
    public LoginResult information() {
        User user = userMapper.selectByPrimaryKey(ThreadContext.userId());
        PersonalInformation pi = personalInformationMapper.selectByUserId(user.getId());
        return bindLoginResult(user,pi);
    }

    @Override
    public void saveInformation(PersonalInformation pi) {
        PersonalInformation dbPi = personalInformationMapper.selectByUserId(ThreadContext.userId());

        personalInformationMapper.updateByPrimaryKeySelective(new PersonalInformation(){{
            setId(dbPi.getId());
            setAvatar(pi.getAvatar());
            setSex(pi.getSex());
            setAge(pi.getAge());
            setEquipment(pi.getEquipment());
            setSignature(pi.getSignature());
            setShowModel(pi.getShowModel());
        }});


        if(!cn.jiguang.common.utils.StringUtils.isEmpty(pi.getAvatar())){
            rongCloudImServer.updateAvatar(ThreadContext.userId(), pi.getAvatar());
        }

    }

    @Override
    public void syncPositioning(PersonalInformation pi) {
        if(pi.getLat() == null || pi.getLat() == null){
            return;
        }

        personalInformationMapper.updateByPrimaryKeySelective(new PersonalInformation(){{
            setId(ThreadContext.userId());
            setLon(pi.getLon());
            setLat(pi.getLat());
        }});
    }


    private String sixNumber(){
        return (Math.random() + "").substring(3,9);
    }


    @Override
    @Cacheable(value = "userInfoCache", key = "#userId")
    public SimpleUser userForId(int userId){
        User user = userMapper.selectByPrimaryKey(userId);
        PersonalInformation pi = personalInformationMapper.selectByPrimaryKey(user.getId());
        return buildSimpleUser(user, pi);
    }

    public SimpleUser buildSimpleUser(User user, PersonalInformation pi){
        SimpleUser simpleUser = new SimpleUser();
        simpleUser.setUserId(user.getId());
        if(pi != null){
            simpleUser.setRealName(pi.getRealName());
            simpleUser.setImToken(user.getrCToken());
            simpleUser.setAvatar(pi.getAvatar());
            simpleUser.setSex(pi.getSex());
            simpleUser.setType(user.getType());
            simpleUser.setEquipment(pi.getEquipment());
            simpleUser.setV(pi.getV());
            simpleUser.setAge(pi.getAge() == null ? 0 : pi.getAge());
            simpleUser.setSignature(pi.getSignature());
            simpleUser.setImId(user.getImId());
            simpleUser.setShowModel(pi.getShowModel() == null ? 3 : pi.getShowModel());
        }
        return simpleUser;
    }


    @Override
    public void applyBigV() {
        PersonalInformation pi = personalInformationMapper.selectByPrimaryKey(ThreadContext.userId());
        if(pi.getV() == 1){
            throw new GeneralException(ms.getMessage("biga.review"));
        }

        BigVApplyExample example = new BigVApplyExample();
        BigVApplyExample.Criteria criteria = example.createCriteria();
        criteria.andUserIdEqualTo(ThreadContext.userId());
        criteria.andStatusEqualTo(1);
        if(applyMapper.countByExample(example) == 1){
            throw new GeneralException(ms.getMessage("biga.review"));
        }

        BigVApply apply = new BigVApply();
        apply.setUserId(ThreadContext.userId());
        apply.setCrtAt(new Date());
        apply.setStatus(1);
        applyMapper.insertSelective(apply);
    }

    @Override
    public LoginResult informationById(Integer userId) {
        User user = userMapper.selectByPrimaryKey(userId);
        PersonalInformation pi = personalInformationMapper.selectByUserId(userId);
        return bindLoginResult(user,pi);
    }


    @Override
    public void updatePassword(UpdatePasswordDto passwordDto) {
        User user = userMapper.selectByPrimaryKey(ThreadContext.userId());

        if(!user.getPassword().equals(encryptionPassword(passwordDto.getPassword()))){
            throw new GeneralException("旧密码错误");
        }

        userMapper.updateByPrimaryKeySelective(new User(){{
            setId(ThreadContext.userId());
            setPassword(encryptionPassword(passwordDto.getNewPassword()));
        }});
    }

    @Override
    public void sendRetrievePassword(UserDto user) {
        if(!phoneExist(user.getPhone())){
            throw new GeneralException(ms.getMessage("phone.no"));
        }

        String code = sixNumber();
        String key  = String.format("ret_%s", user.getPhone());
        AliYunSMS.sendRegisterCustomerUserSMS(user.getPhone(), code);
        cacheService.sendSmsVerCode(ThreadContext.ip(), key, code);
//        cacheService.sendSmsVerCode(ThreadContext.ip(), key, "123456");
    }

    @Override
    public void retrievePassword(UserDto user) {
        String verCode = cacheService.getSmsVerCode(String.format("ret_%s", user.getPhone()));
        if(StringUtils.isEmpty(verCode)){
            throw new GeneralException(ms.getMessage("sms.code.invalid"));
        }else if(!verCode.equals(user.getVerCode())){
            throw new GeneralException(ms.getMessage("sms.code.error"));
        }

        User dbUser = selectUserByPhone(user.getPhone());

        userMapper.updateByPrimaryKeySelective(new User(){{
            setId(dbUser.getId());
            setPassword(encryptionPassword(user.getPassword()));
        }});
    }

    @Override
    public void pushPost(int userId) {
        personalInformationMapper.pushPost(userId);
    }


    @Override
    public void apertureSetting(UserApertureSet apertureSet) {
        UserApertureSet dbSet = userApertureSetMapper.selectByPrimaryKey(ThreadContext.userId());
        if(dbSet == null){
            apertureSet.setUserId(ThreadContext.userId());
            userApertureSetMapper.insertSelective(apertureSet);
        }else{
            apertureSet.setUserId(ThreadContext.userId());
            userApertureSetMapper.updateByPrimaryKeySelective(apertureSet);
        }
    }


    @Override
    public List<Map<String, Object>> searchByName(CommonDto commonDto) {
        return personalInformationMapper.searchByName(commonDto.getName());
    }
}
